﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using WWB.SitePointPrivateMessages.Core.Model.Messages;

namespace WWB.SitePointPrivateMessages.Core.Service
{
    public interface IMessageSearcher
    {
        void IndexMessages(IEnumerable<PrivateMessage> messages);
        IEnumerable<PrivateMessage> SearchMessages(string searchFor);
        IEnumerable<PrivateMessage> SearchMessages(string searchFor, bool includeUsers);
    }

    public class LuceneMessageSearcher : IMessageSearcher
    {
        private readonly Directory luceneStore;
        private readonly Analyzer az;
        private IList<PrivateMessage> messageStore;

        private IndexSearcher searcher;

        public LuceneMessageSearcher(Directory luceneStore, Analyzer az)
        {
            if (luceneStore == null) throw new ArgumentNullException("luceneStore");
            if (az == null) throw new ArgumentNullException("az");
            this.luceneStore = luceneStore;
            this.az = az;
        }

        public void IndexMessages(IEnumerable<PrivateMessage> messages)
        {
            var writer = new IndexWriter(luceneStore, az, true);
            messageStore = new List<PrivateMessage>();
            foreach (PrivateMessage pm in messages)
            {
                var doc = createDocumentFrom(pm);
                writer.AddDocument(doc);
                messageStore.Add(pm);
            }
            writer.Close();
        }

        private Document createDocumentFrom(PrivateMessage pm)
        {
            var d = new Document();
            d.Add(new Field("FingerPrint", pm.FingerPrint, Field.Store.YES, Field.Index.NO));
            d.Add(new Field("Title", pm.Title, Field.Store.NO, Field.Index.TOKENIZED));
            d.Add(new Field("Body", pm.Body, Field.Store.NO, Field.Index.TOKENIZED));
            d.Add(new Field("FromUser", pm.Body, Field.Store.NO, Field.Index.UN_TOKENIZED));
            d.Add(new Field("ToUsers", pm.ToUserNames, Field.Store.NO, Field.Index.TOKENIZED));
            return d;
        }

        public IEnumerable<PrivateMessage> SearchMessages(string searchFor)
        {
            return SearchMessages(searchFor, false);
        }

        public IEnumerable<PrivateMessage> SearchMessages(string searchFor, bool includeUsers)
        {
            List<string> fields = new List<string>(){"Title", "Body"};
            if (includeUsers)
            {
                fields.AddRange(new []{"FromUser", "ToUsers"});
            }
            MultiFieldQueryParser mfqp = new MultiFieldQueryParser(fields.ToArray(), az);
            if (searcher == null)
            {
                searcher = new IndexSearcher(luceneStore);
            }
            var hits = searcher.Search(mfqp.Parse(searchFor));
            return convertFromHits(hits);
        }

        private IEnumerable<PrivateMessage> convertFromHits(Hits hits)
        {
            var ret = new List<PrivateMessage>();
            for (int i = 0; i < hits.Length(); i++)
            {
                var msg =
                    messageStore.FirstOrDefault(x => x.FingerPrint == hits.Doc(i).GetField("FingerPrint").StringValue());
                if (msg != null)
                {
                    ret.Add(msg);
                }
            }
            return ret;
        }
    }
}
